﻿using System;
using System.Collections.Generic;
using FluffyUnderware.Curvy;
using LogSystem;
using NpcFramework;
using UnityEngine;
using UnityEngine.Rendering;
using Yoozoo.Managers.ResourceManagerV2.Runtime;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;

namespace Yoozoo.Gameplay.Liberty.AI
{

    [Flags]
    public enum NextPointFlags
    {
        
        
    }


    public enum PointDirection
    {
        None = 0,
        Forward = 1,
        Backward = 2,
    }
    
    
    public class LibertyPathManager : MonoBehaviour
    {
        private const float AREA_SIZE = 40;

        private Dictionary<int, PathData> _pathMap = new Dictionary<int, PathData>();

        private Dictionary<int, List<PathPointData>> _areaPointMap = new Dictionary<int, List<PathPointData>>();
        
        private ResLoader pathLoader;

        private List<Transform> _editorNpcPaths;

        private Transform _npcPathRoot;
        private Transform _carSplineRoot;

        private Dictionary<string, GameObject> carSplineAssets = 
            new Dictionary<string, GameObject>(15);

        private LibertyTrafficData _trafficData;

        public void Preload(LibertyTrafficData trafficData, Action callBack)
        {
            _trafficData = trafficData;
            List<PathData> pathDataList = new List<PathData>(30);
            pathDataList.AddRange(trafficData.npcPathList);
            pathDataList.AddRange(trafficData.carPathList);
            InitPaths(pathDataList);

            _npcPathRoot = new GameObject("npcPathRoot").transform;
            _npcPathRoot.SetParent(this.transform);
            
            _carSplineRoot = new GameObject("carSplineRoot").transform;
            _carSplineRoot.SetParent(this.transform);
            
            pathLoader = ResLoader.Alloc();
            _editorNpcPaths = new List<Transform>();
            
#if UNITY_EDITOR
            // 编辑模式下将npc路径的prefab也加载进来方便调试

            for (int i = 0; i < pathDataList.Count; i++)
            {
                PathData pathData = pathDataList[i];
                if (!string.IsNullOrEmpty(pathData.prefabPath))
                {
                    pathLoader.Add2Load(pathData.prefabPath, (s, an, a) =>
                    {
                        Transform npcPath = GameObject.Instantiate(a as GameObject, _npcPathRoot).transform;
                        npcPath.position = pathData.position;
                        _editorNpcPaths.Add(npcPath);
                    });
                }
            }
            pathLoader.Load(() =>
            {
                callBack?.Invoke();
            });
#else
            callBack?.Invoke();
#endif


        }

        public void Dispose()
        {
            if (pathLoader != null)
            {
                pathLoader.Recycle2Cache();
                pathLoader = null;
            }
            
            carSplineAssets.Clear();
        }
        
        
        public void InitPaths(List<PathData> paths)
        {
            foreach (var path in paths)
            {
                AddPath(path);
            }
        }

        public void AddPath(PathData path)
        {
            _pathMap[path.id] = path;
            path.Init();
            foreach (var point in path.pathPoints)
            {
                int idx = GetAreaIndex(point.position);
                if (!_areaPointMap.ContainsKey(idx))
                {
                    _areaPointMap[idx] = new List<PathPointData>();
                }
                _areaPointMap[idx].Add(point);
            }
            
        }

        public PathData GetPath(int id, PathType pathType = PathType.None)
        {
            if (_pathMap.TryGetValue(id,out var pathData))
            {
                if (pathType == PathType.None)
                {
                    return pathData;
                }
                else if (pathType == pathData.pathType)
                {
                    return pathData;
                }
            }
            return null;
        }

        public PathPointData GetPathPoint(int pathId, int pointIndex)
        {
            PathData pathData;
            if (_pathMap.TryGetValue(pathId,out pathData))
            {
                if (pathData.pathPoints.Count > pointIndex)
                {
                    return pathData.pathPoints[pointIndex];
                }
            }
            return null;
        }

        public PathPointData GetTerminalPoint(int pathId)
        {
            PathData pathData;
            if (_pathMap.TryGetValue(pathId, out pathData))
            {
                if (pathData.loop)
                {
                    return null;
                }

                return pathData.pathPoints[pathData.pathPoints.Count - 1];
            }
            return null;
        }

        public PathPointData GetNextPoint(int pathId,int pointIndex,PathDirection pathDirection)
        {

            _pathMap.TryGetValue(pathId, out PathData pathData);
            if (pathData!=null)
            {
                if (pathDirection == PathDirection.Forward)
                {
                    return pathData.pathPoints[pathData.pathPoints[pointIndex].nextIndex];
                }
                else
                {
                    return pathData.pathPoints[pathData.pathPoints[pointIndex].prevIndex];
                }
            }
            
            
            // TODO 根据AI朝向，找到一定半径范围内夹角小于90度的其它路径开始点或结束点，

            return null;
        }

        public PathPointData GetClosestPathPoints(int pathId,Vector3 pos)
        {
            _pathMap.TryGetValue(pathId, out PathData pathData);
            if (pathData == null) return null;
            
            float minDis = 99999;
            int minPointIndex = -1;
            for (int i = 0; i < pathData.pathPoints.Count; i++)
            {
                float dis = Vector3.Distance(pathData.pathPoints[i].position, pos);
                if ( dis < minDis)
                {
                    minDis = dis;
                    minPointIndex = i;
                }
            }
            if (minPointIndex < 0)
            {
                return null;
            }
            
            return pathData.pathPoints[minPointIndex];
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="forward">Ai的朝向，用于决定延路径行走的方向</param>
        /// <param name="pathDirection"></param>
        /// <returns></returns>
        public PathPointData GetNearestNavPoint(Vector3 pos,Vector3 forward,out PathDirection pathDirection)
        {
            PathPointData pointData = GetNearestPointByArea(pos,PathType.Npc);
            pathDirection = PathDirection.Forward;
            if (pointData!=null)
            {
                var nextPoint = GetNextPoint(pointData.pathId, pointData.pointIndex, PathDirection.Forward);
                if (nextPoint!=null)
                {
                    float angle = Vector3.Angle((nextPoint.position - pointData.position).normalized, forward.normalized);
                    if (angle > 90)
                    {
                        pathDirection = PathDirection.Backward;
                    }
                }
            }
            return pointData;
        }

        public PathPointData GetNearestPoint(Vector3 pos,PathType pathType)
        {
            return GetNearestPoint(pos, Vector3.one, pathType);
        }
        
        public PathPointData GetNearestPoint(Vector3 pos, Vector3 forward, PathType pathType, PointDirection pointDirection = PointDirection.None)
        {
            float min = 99999;

            PathPointData pointData = null;
            
            foreach (var pathDataPair in _pathMap)
            {
                if (pathDataPair.Value.pathType == pathType)
                {
                    foreach (var pathPointData in pathDataPair.Value.pathPoints)
                    {
                        bool b = true;

                        if (pointDirection != PointDirection.None)
                        {
                            float angle = Vector3.Angle(pathPointData.position - pos, forward);
                            if (pointDirection == PointDirection.Forward)
                            {
                                if (angle >= 90)
                                {
                                    b = false;
                                }
                            }
                            else if (pointDirection == PointDirection.Backward)
                            {
                                if (angle < 90)
                                {
                                    b = false;
                                }
                            }
                        }
                        
                        if (b)
                        {
                            float dis = Vector3.Distance(pathPointData.position, pos);
                            if (dis < min)
                            {
                                min = dis;
                                pointData = pathPointData;
                            }
                        }
                       
                    }
                }
            }
            
            return pointData;
        }
        
        public PathPointData GetNearestPointByArea(Vector3 pos, PathType pathType)
        {
            return GetNearestPointByArea(pos, Vector3.one, pathType);
        }

        public PathPointData GetNearestPointByArea(Vector3 pos, Vector3 forward, PathType pathType, PointDirection pointDirection = PointDirection.None)
        {
            PathPointData pointData = null;

            float min = 999999f;
            
            for (int i = -1; i <= 1; i++)
            {
                for (int j = -1; j <= 1; j++)  // 周围格子区域
                {
                    Vector3 p = pos;
                    p.x += i * AREA_SIZE;
                    p.z += j * AREA_SIZE;

                    int areaIndex = GetAreaIndex(p);

                    if (_areaPointMap.TryGetValue(areaIndex,out var pointDataList))
                    {
                        foreach (var data in pointDataList)
                        {
                            if (data.pathType == pathType)
                            {
                                bool b = true;

                                if (pointDirection != PointDirection.None)
                                {
                                    float angle = Vector3.Angle(data.position - pos, forward);
                                    if (pointDirection == PointDirection.Forward)
                                    {
                                        if (angle >= 90)
                                        {
                                            b = false;
                                        }
                                    }
                                    else if (pointDirection == PointDirection.Backward)
                                    {
                                        if (angle < 90)
                                        {
                                            b = false;
                                        }
                                    }
                                }

                                if (b)
                                {
                                    float d = Vector3.Distance(data.position, pos);
                                    if (d < min)
                                    {
                                        min = d;
                                        pointData = data;
                                    }
                                }
                               
                            }
                        }
                    }
                    
                }
            }
            
            if (pointData == null)  // 找不到就全局找
            {
                pointData = GetNearestPoint(pos,forward, pathType,pointDirection);
            }
            
            return pointData;
        }



        public PathPointData GetReturnPathPoint(int pathId, int pointIndex, PathDirection pathDirection,Vector3 pos)
        {
            int currentPointIdx = pointIndex;
            int i = 0;
            PathPointData pointData = null;
            while (i <= 4)
            {
                var cp = GetPathPoint(pathId, currentPointIdx);
                var np = GetNextPoint(pathId, currentPointIdx, pathDirection);
                if (np == null)
                {
                    break;
                }
                float angel = Vector3.Angle((np.position - cp.position).normalized, (np.position - pos));
                if (angel <= 90)
                {
                    pointData = np;
                    break;
                }
                i++;
            }

            if (pointData == null)
            {
                pointData = GetNearestPoint(pathId, pos);
            }

            return pointData;
        }


        public PathPointData GetNearestPoint(int pathId, Vector3 pos)
        {
            float min = 99999;

            PathPointData pointData = null;

            _pathMap.TryGetValue(pathId, out var pd);
            if (pd!=null)
            {
                foreach (var pathPointData in pd.pathPoints)
                {
                    float dis = Vector3.Distance(pathPointData.position, pos);
                    if (dis < min)
                    {
                        min = dis;
                        pointData = pathPointData;
                    }
                }
            }
            return pointData;
        }
        
        
        
        private int GetAreaIndex(Vector3 pointPos)
        {
            return ((int)(pointPos.x / AREA_SIZE)) * 100000 + ((int)(pointPos.z / AREA_SIZE));
        }
        

        /// <summary>
        /// 随机逃跑目标点
        /// </summary>
        /// <returns></returns>
        public PathPointData RandomEscapeDestination(Vector3 currentPosition)
        {
            int currentAreaIndex = GetAreaIndex(currentPosition);
            
            List<PathPointData> pointDatas;

            // 搜索周围的有效区域
            List<List<PathPointData>> randomPointDatasList = ListPool<List<PathPointData>>.Get();
            for (int i = -10; i <= 10; ++i)
            {
                if (i == 0)
                {
                    continue;
                }
                
                if (_areaPointMap.TryGetValue(currentAreaIndex+i, out pointDatas))
                {
                    if (pointDatas.Count > 0)
                    {
                        randomPointDatasList.Add(pointDatas);
                    }
                }
            }
            
            // 在周围有效区域内随机
            if (randomPointDatasList.Count > 0)
            {
                int random = Random.Range(0, randomPointDatasList.Count);
                // Debug.LogError(LogModule.LibertySceneBattle, $"随机到逃跑目标点: 当前区域:{currentAreaIndex}, 目标区域:{randomAreaIndex}");
                pointDatas = randomPointDatasList[random];
                ListPool<List<PathPointData>>.Release(randomPointDatasList);
                return pointDatas[Random.Range(0, pointDatas.Count)];
            }

            ListPool<List<PathPointData>>.Release(randomPointDatasList);
            
            // 没有找到则在当前区域随机
            if (_areaPointMap.TryGetValue(currentAreaIndex, out pointDatas))
            {
                if (pointDatas.Count > 0)
                {
                    // Debug.LogError(LogModule.LibertySceneBattle, $"未随机到逃跑目标点: 当前区域:{currentAreaIndex}");
                    return pointDatas[Random.Range(0, pointDatas.Count)];
                }
            }
            
            return null;
        }

    }
}